package net.wangds.procengine.flow;

import net.wangds.log.helper.LogHelper;
import net.wangds.procengine.ProcResEnum;
import net.wangds.procengine.flow.define.FlowDef;
import net.wangds.procengine.flow.define.actor.ActorDef;
import net.wangds.procengine.flow.define.actor.AnonymousActorDef;
import net.wangds.procengine.flow.define.node.FlowNode;
import net.wangds.procengine.flow.instance.FlowInstance;
import net.wangds.procengine.flow.instance.actor.Actor;
import net.wangds.procengine.flow.instance.context.HashTableContext;
import net.wangds.procengine.flow.instance.step.FlowStep;
import org.apache.commons.lang3.StringUtils;

import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

/**
 * 流程引擎.
 */
public class FlowEngine {

    /**
     * 所有流程节点列表.
     */
    private static final List<FlowOperator> FLOW_OPERATORS = new LinkedList<>();

    static class CombineFlowOperator implements FlowOperator {

        @Override
        public <A extends ActorDef> Optional<FlowDef<A>> findFlowDefById(String flowDefId) {
            for(FlowOperator op:FLOW_OPERATORS){
                Optional<FlowDef<A>> res = op.findFlowDefById(flowDefId);
                if(res.isPresent()){
                    return res;
                }
            }
            return Optional.empty();
        }

        @Override
        public <C extends FlowContext, A extends ActorDef> Optional<FlowInstance<C, A>> createFlowInstance(Actor actor, FlowDef<A> def) {
            for(FlowOperator op:FLOW_OPERATORS){
                Optional<FlowInstance<C, A>> res = op.createFlowInstance(actor, def);
                if(res.isPresent()){
                    return res;
                }
            }
            return Optional.empty();
        }

        @Override
        public <C extends FlowContext, A extends ActorDef> Optional<FlowStep<C>> generateFlowStep(FlowInstance<C, A> instance, FlowNode node, Actor actor) {
            for(FlowOperator op:FLOW_OPERATORS){
                Optional<FlowStep<C>> res = op.generateFlowStep(instance, node, actor);
                if(res.isPresent()){
                    return res;
                }
            }
            return Optional.empty();
        }

    }

    private static final CombineFlowOperator combineFlowOperator = new FlowEngine.CombineFlowOperator();

    /**
     * 注册流程操作组件.
     * @param fo 流程操作组件.
     */
    public static void registorFlowOperator(FlowOperator fo){
        if(fo!=null){
            synchronized (FLOW_OPERATORS){
                if(!FLOW_OPERATORS.contains(fo)) {
                    FLOW_OPERATORS.add(fo);
                }
            }
        }
    }



    public static <C extends FlowContext, A extends ActorDef> ProcResEnum start(C ctx, Actor actor, FlowDef<A> flowDef) {



        Optional<FlowInstance<FlowContext, A>> op = combineFlowOperator.createFlowInstance(actor, flowDef);
        if(ctx==null){
            ctx = (C)new HashTableContext();
        }
        //LogHelper.dev("---------------"+flowDef);
        ProcResEnum res;
        if (op.isPresent()) {
            FlowInstance<FlowContext, A> instance;
            instance = op.get();
            //LogHelper.dev("---------------instance:"+instance);
            instance.setContext(ctx);
            res = instance.start();
        } else {
            LogHelper.debug("创建流程实例为null，返回流程结束.");
            res = ProcResEnum.FINISH;
        }

        return res;
    }


    public static <C extends FlowContext, A extends ActorDef> ProcResEnum start(C ctx, Actor actor, String flowDefId) {
        Optional<FlowDef<A>> def = combineFlowOperator.findFlowDefById(flowDefId);
        if(def.isPresent()){
            return start(ctx, actor, def.get());
        }else{
            LogHelper.warn("无法找到编号为\""+flowDefId+"\"的流程定义");
        }
        return ProcResEnum.FINISH;
    }

    public static <C extends FlowContext, A extends ActorDef>
    ProcResEnum  run(C ctx, Actor actor, FlowInstance<C, A> instance, FlowNode node){

        //LogHelper.dev("执行节点！下一个："+node.getNextId());

        ActorDef actorDef = node.getActorDef();
        if(actorDef==null){
            actorDef = new AnonymousActorDef();
        }
        if(actorDef.validate(actor)){

            ProcResEnum res = ProcResEnum.CONTINUE;

            do {
                Optional<FlowStep<C>> stepOp = combineFlowOperator.generateFlowStep(instance, node, actor);

                if (stepOp.isPresent()) {
                    FlowStep<C> step = stepOp.get();
                    instance.setCurrentStep(step);
                    step.proc(ctx);
                    res = step.getRes();
                    if (res == null) {
                        res = ProcResEnum.CONTINUE;
                    }
                    step.setRes(res);

                    if(res==ProcResEnum.CONTINUE){
                        //设置下一步
                        String nextId = node.getNextId();
                        if(StringUtils.isBlank(nextId)){
                            //没有定义下一步的认为流程已经结束
                            res = ProcResEnum.FINISH;
                        }else {
                            Optional<FlowNode> nextNodeOp = combineFlowOperator.findFlowNode(instance, nextId);
                            if(nextNodeOp.isPresent()){
                                node = nextNodeOp.get();
                            } else {
                                //下一步节点不存在.
                                LogHelper.dev("下一节点不存在:"+nextId);
                                res = ProcResEnum.FINISH;
                            }
                        }
                    }

                } else {
                    res = ProcResEnum.FINISH;
                }

            }while(res==ProcResEnum.CONTINUE);

            return res;
        }else{
            return ProcResEnum.NO_PRIVILEGE;
        }
    }


    public static <C extends FlowContext> ProcResEnum  run(C ctx, Actor actor, String flowDefId){
        //    throw new UnsupportedOperationException();

        Optional<FlowDef<ActorDef>> def = combineFlowOperator.findFlowDefById(flowDefId);
        if(def.isPresent()){
            return start(ctx, actor, def.get());
        }else{
            LogHelper.warn("无法找到编号为\""+flowDefId+"\"的流程定义");
        }
        return ProcResEnum.FINISH;

    }





}
